package com.farmUni.utils;


import com.farmUni.entity.LuckyArticle;
import com.farmUni.entity.LuckyUser;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Recommend {
    private Map<Double, Long> computeNearestNeighbor(Long userId, List<LuckyUser> LuckyUsers) {
        Map<Double, Long> distances = new TreeMap<>();

        LuckyUser u1 = new LuckyUser(userId);
        for (LuckyUser LuckyUser:LuckyUsers) {
            if (userId.equals(LuckyUser.id)) {
                u1 = LuckyUser;
            }
        }

        for (int i = 0; i < LuckyUsers.size(); i++) {
            LuckyUser u2 = LuckyUsers.get(i);

            if (!u2.id.equals(userId)) {
                double distance = pearson_dis(u2.articleList, u1.articleList);
                distances.put(distance, u2.id);
            }

        }
        System.out.println("该用户与其他用户的皮尔森相关系数 -> " + distances);
        return distances;
    }


    /**
     * 计算2个打分序列间的pearson距离
     * 选择公式四进行计算
     * @param rating1
     * @param rating2
     * @return
     */
    private double pearson_dis(List<LuckyArticle> rating1, List<LuckyArticle> rating2) {
        int n=rating1.size();
        List<Integer> rating1ScoreCollect = rating1.stream().map(A -> A.score).collect(Collectors.toList());
        List<Integer> rating2ScoreCollect = rating2.stream().map(A -> A.score).collect(Collectors.toList());
        if(rating1ScoreCollect.size()>rating2ScoreCollect.size()){
            int i1 = rating1ScoreCollect.size() - rating2ScoreCollect.size();
            for (int i = 0; i < i1; i++) {
                rating2ScoreCollect.add(0);
            }
        }else if(rating1ScoreCollect.size()<rating2ScoreCollect.size()){
            int i1 = rating2ScoreCollect.size() - rating1ScoreCollect.size();
            for (int i = 0; i < i1; i++) {
                rating1ScoreCollect.add(0);
            }
        }
        double Ex= rating1ScoreCollect.stream().mapToDouble(x->x).sum();
        double Ey= rating2ScoreCollect.stream().mapToDouble(y->y).sum();
        double Ex2=rating1ScoreCollect.stream().mapToDouble(x->Math.pow(x,2)).sum();
        double Ey2=rating2ScoreCollect.stream().mapToDouble(y->Math.pow(y,2)).sum();
        double Exy= IntStream.range(0,n).mapToDouble(i->rating1ScoreCollect.get(i)*rating2ScoreCollect.get(i)).sum();
        double numerator=Exy-Ex*Ey/n;
        double denominator=Math.sqrt((Ex2-Math.pow(Ex,2)/n)*(Ey2-Math.pow(Ey,2)/n));
        if (denominator==0) return 0.0;
        return numerator/denominator;
    }


    public List<LuckyArticle> recommend(Long userId, List<LuckyUser> LuckyUsers) {
        //找到最近邻
//        Map<Double, Long> distances = computeNearestNeighbor(userId, LuckyUsers);
//        Long nearest = distances.values().iterator().next();
//        System.out.println("最近邻 -> " + nearest.toString());
        Map<Double, Long> distances = computeNearestNeighbor(userId, LuckyUsers);
        Iterator<Map.Entry<Double, Long>> iterator = distances.entrySet().iterator();
        Map.Entry<Double, Long> next = null;
        while (iterator.hasNext()) {
            next = iterator.next();
        }
        Long nearest = next.getValue();
        System.out.println("最近邻 -> " + nearest);

        //找到最近邻看过，但是我们没看过的电影，计算推荐
        LuckyUser neighborRatings = new LuckyUser();
        for (LuckyUser LuckyUser:LuckyUsers) {
            if (nearest.equals(LuckyUser.id)) {
                neighborRatings = LuckyUser;
            }
        }
        System.out.println("最近邻看过的资讯 -> " + neighborRatings.articleList);

        LuckyUser LuckyUserRatings = new LuckyUser();
        for (LuckyUser LuckyUser:LuckyUsers) {
            if (userId.equals(LuckyUser.id)) {
                LuckyUserRatings = LuckyUser;
            }
        }
        System.out.println("用户看过的资讯 -> " + LuckyUserRatings.articleList);

        //根据自己和邻居的电影计算推荐的资讯
        List<LuckyArticle> recommendationMovies = new ArrayList<>();
        for (LuckyArticle luckyArticle : neighborRatings.articleList) {
            if (LuckyUserRatings.find(luckyArticle.articleId) == null) {
                recommendationMovies.add(luckyArticle);
            }
        }
        Collections.sort(recommendationMovies);
        return recommendationMovies;
    }
}

